<?php
/**
 * @package		简易CMS
 * @subpackage	错误陷阱处理类
 * @version		$Id: class_error.php 8 2012-06-07 12:37:35Z htmambo@gmail.com $
 * @author		Hoping
 * @copyright	Copyright (C) 2010 Hoping Software Studio.
 */

if (!defined('IN_JYCMS')) {
	exit('Access Denied');
}

class cmsError {
	static $ret = false;
	static $type = 'system';

	function db_error($message, $sql) {
		global $db;
		self::$type = 'db';
		list($showtrace, $logtrace) = cmsError::debug_backtrace();
		$title = lang('error', 'db_' . $message);
		$title_sql = lang('error', 'db_query_sql');

		if (empty($db))
			$db = &DB::object();
		$dberrno = $db->errno();
		//清除系统数据库名、表前缀
		$dberror = str_replace(array($db->tablepre, $db->config['dbname'].'.'), '', $db->error());
		$sql = htmlspecialchars(str_replace($db->tablepre, '', $sql));
		$sql = cmsError::sqlhighlight($sql);

		$msg = '<li>[Type] ' . $title . '</li>';
		$msg .= $dberrno ? '<li>[' . $dberrno . '] ' . $dberror . '</li>' : '';
		$msg .= $sql ? '<li>['.$title_sql.'] ' . $sql . '</li>' : '';

		cmsError::show_error($msg, $showtrace, false);
		unset($msg);

		$errormsg = '<b>' . $title . '</b>';
		$errormsg .= "[$dberrno]<br /><b>ERR:</b> $dberror<br />";
		if ($sql) {
			$errormsg .= '<b>SQL:</b> ' . $sql;
		}
		$errormsg .= "<br />";
		$errormsg .= '<b>PHP:</b> ' . $logtrace;

		cmsError::write_error_log($errormsg);
		exit();
	}

	function system_error($message, $show = true, $save = true, $halt = true) {
		if (!empty($message)) {
			$msg = lang('error', $messsage);
			if($msg) $message = $msg;
		} else {
			$message = lang('error', 'error_unknow');
		}
		$message = str_replace(ROOT_PATH, '', $message);
		list($showtrace, $logtrace) = cmsError::debug_backtrace();
		if(self::$ret) return false;
		if ($save) {
			$messagesave = '<b>' . $message . '</b><br><b>PHP:</b>' . $logtrace;
			cmsError::write_error_log($messagesave);
		}

		if ($show) {
			cmsError::show_error("<li>$message</li>", $showtrace, 0);
		}

		if ($halt) {
			exit();
		} else {
			return $message;
		}
	}

	function template_error($message, $tplname) {
		self::$type = 'template';
		$message = lang('error', $message);
		$tplname = str_replace(ROOT_PATH, '', $tplname);
		$message = $message . ': ' . $tplname;
		cmsError::system_error($message);
	}

	function debug_backtrace() {
		$skipfunc[] = 'cmsError->debug_backtrace';
		$skipfunc[] = 'cmsError->db_error';
		$skipfunc[] = 'cmsError->template_error';
		$skipfunc[] = 'cmsError->system_error';
		$skipfunc[] = 'db_mysql->halt';
		$skipfunc[] = 'db_mysql->query';
		$skipfunc[] = 'DB::_execute';
		$skipfunc[] = 'system_error';
		$skipfunc[] = 'DB->query';
		$skipfunc[] = 'error_report';
		//$skipfunc[] = 'cmsFS::check';
		$show = '';
		$log = '';
		$debug_backtrace = debug_backtrace();
		krsort($debug_backtrace);
		$lastfile = $line = '';
		$no = 1;
		foreach ($debug_backtrace as $error) {
			$func = isset($error['class']) ? $error['class'] : '';
			$func .= isset($error['type']) ? $error['type'] : '';
			$func .= isset($error['function']) ? $error['function'] : '';
			if (in_array($func, $skipfunc)) break;

			$file = str_replace(ROOT_PATH, '', $error['file']);
			if($file) {
				$lastfile = $error['file'];
				$line = $error['line'];
			}
			$error['line'] = sprintf('%04d', $error['line']);
			$show .= "<li><span><b>$no</b></span>:[Line: $error[line]]" . $file . "($func)</li>";
			$no++;
			$log .= ! empty($log) ? ' -> ' : '';
			$log .= $file . ':' . $error['line'] . ':' . $error['line'];
		}
		$lines = file_exists($lastfile)?file($lastfile):null;
		if ($lines){
			if(CMS_DEBUG && @constant('CMS_DEBUG')){
				$show = substr($show, 0, -5);
				$show .= cmsError::getlines($lines, $line);
				$show .= '</li>';
			}
		}
		return array($show, $log);
	}

	function getlines($var, $line){
		$result = "<ul class=\"code\">";
		$result .= '<li class="title">代码片段：</li>';
		$strlen = sizeof($var);
		$il = strlen($line);
		if (($line%10)>=5)
		$il ++;
		$i = $line-5;
		for(; $i<=$line+5; ++ $i){
			if ((1<=$i)&&($i<=$strlen)){
				$html = @rtrim(@htmlspecialchars($var[$i-1]));
				$html = str_replace("\t", "&nbsp;&nbsp;&nbsp;", $html);
				$html = str_replace(" ", "&nbsp;", $html);
				$i = sprintf("%0".$il."d", $i);
				if ($i!=$line){
					$result .= "<li><span><b>{$i}</b></span> {$html}</li>\n";
				} else{
					$result .= "<li class=\"mark\"><span>".$i."</span> {$html}</li>\n";
				}
			}
		}
		$result .= "</ul>";
		return $result;
	}

	function sqlhighlight($sql) {
		$sql=preg_replace("/[\s\r\n\t]+/"," ",$sql);
		#格式化
		#常用命令
		$format="delete|update|replace|select|from|where|group by|order by|limit|having";
		$sql=preg_replace("/[\s\r\n\t]*($format)[\s\r\n\t]+/i","\r\n\\1\r\n\t",$sql);
		#操作方式
		$format="inner join|left join|right join|and|or";
		$sql=preg_replace("/[\s\r\n\t]+($format)[\s\r\n\t]+/i","\r\n\t\\1\r\n\t\t",$sql);
		#关键词
		$keyword="into|select|insert|update|table|delete|set|from|inner|left|right|join|on|order|by|asc|desc|hiving|group|by|limit|where|and|or|\=|>|<";
		$sql=preg_replace("/(\b($keyword)\b)/i","<font color=blue><b>\\1</b></font>",$sql);
		#函数
		$sql=preg_replace("/(\b([a-z0-9_]+))\(/i","<font color=#FF33FF><b>\\1</b></font>(",$sql);
		#字符串
		$sql=preg_replace("/(([\"']).*?\\2)/","<font color=red><b>\\1</b></font>",$sql);
		return $sql;
	}

	function show_error($errormsg, $phpmsg = '', $exit = false) {
		global $_G;
		ob_end_clean();
		$gzip = getglobal('gzipcompress');
		ob_start($gzip ? 'ob_gzhandler' : null);
		$host = $_SERVER['HTTP_HOST'];
		$phpmsg = trim($phpmsg);
		$title = ucwords(self::$type);
		echo <<<EOT
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
	<title>$host - $title Error</title>
	<meta http-equiv="Content-Type" content="text/html; charset={$_G['config']['output']['charset']}" />
	<meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" />
	<style type="text/css">
	<!--
	body { background-color: white; color: black; }
	#container { width: 100%; background: #fcfcfc;}
	#message   { min-width: 650px; color: black; background-color: #FFFFCC; padding: 15px; }
	#bodytitle { font: 13pt/15pt verdana, arial, sans-serif; height: 35px; vertical-align: top; }
	.bodytext  { font: verdana, arial, sans-serif; font-weight: bold;}
	.help  { font: 12px verdana, arial, sans-serif; color: red;}
	.red  {color: red;}
	ul {margin: 0;}
	a:link     { font: verdana, arial, sans-serif; color: red; }
	a:visited  { font: verdana, arial, sans-serif; color: #4e4e4e; }
	ul.code {
		padding: 0; margin: 0;
		font-family: Courier, Serif;
		list-style: none;
		border-top: 1px solid #ccc;
		border-bottom: 1px solid #fff;
	}
	ul.code li {
		padding:0 5px;
		border-top: 1px solid #fff;
		border-bottom: 1px solid #ccc;
		text-align: left;
		background:#f0f0f0;
		line-height: 24px;
		overflow: hidden;
		white-space:nowrap;
	}
	ul.code li.title {
		font-size: 14px;
		font-weight: bold;
		text-align: left;
		color: #ff0000;
	}
	ul.code li.mark {
		color: #a80000;
		font-weight: bold;
		font-style: italic;
		background: #fafafa;
	}
	-->
	</style>
</head>
<body>
<table cellpadding="1" cellspacing="5" id="container">
<tr>
	<td id="bodytitle" width="100%">JYCMS $title Error </td>
</tr>
EOT;

		if (self::$type == 'db') {
			echo '<tr><td class="bodytext">'.lang('error', 'The database has encountered a problem.').'</td></tr>';
		} else {
			echo '<tr><td class="bodytext">'.lang('error', 'Your request has encountered a problem.').' </td></tr>';
		}

		echo '<tr><td><hr size="1"/></td></tr><tr><td class="bodytext">'.lang('error', 'Error messages').': </td></tr>';
		echo '<tr><td id="message"><ul> '.$errormsg.'</ul></td></tr>';

		if (!empty($phpmsg)) {
			echo '<tr><td class="bodytext">'.lang('error', 'Program messages').': </td></tr><tr>';
			echo '<td><ul> '.$phpmsg.' </ul></td></tr>';
		}

		$endmsg = lang('error', 'error_end_message', array('host' => $host));
		echo '<tr><td class="help">'.$endmsg.'</td></tr></table></body></html>';
		if($exit) exit;
	}

	function clear($message) {
		return str_replace(array("\t", "\r", "\n"), " ", $message);
	}

	function write_error_log($message) {
		$message = cmsError::clear($message);
		$time = time();
		$file = ROOT_PATH . './data/log/' . date("Ym") . '_errorlog.php';
		$hash = md5($message);
		$mainframe = & cmsCore::instance();
		$user = '<b>User:</b> uid=' . intval($mainframe->uid) . '; IP=' . $mainframe->var['clientip'] . '; RIP:' . $_SERVER['REMOTE_ADDR'];
		$uri = 'Request: ' . htmlspecialchars(cmsError::clear($_SERVER['REQUEST_URI']));
		$message = "<?PHP exit;?>\t{$time}\t$message\t$hash\t$user $uri\n";
		if ($fp = @fopen($file, 'rb')) {
			$lastlen = 10000;
			$maxtime = 60 * 10;
			$offset = filesize($file) - $lastlen;
			if ($offset > 0) {
				fseek($fp, $offset);
			}
			if ($data = fread($fp, $lastlen)) {
				$array = explode("\n", $data);
				if (is_array($array))
					foreach ($array as $val) {
						$row = explode("\t", $val);
						if ($row[0] != '<?PHP exit;?>')
							continue;
						if ($row[3] == $hash && ($row[1] > $time - $maxtime)) {
							return;
						}
					}
			}
		}
		@error_log($message, 3, $file);
	}

}